/*
 *  SyncIT , easy synchronization of files between computers and devices.
 *  Copyright (C) 2011  Christian Masus <cmasus(at)gmail.com>
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version. If redistributing and/or modifying
 *  this file, this notice must be included in the redistributed or
 *  modified copy.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package gui.javagui;

import java.awt.Color;
import java.awt.Component;
import java.awt.GridLayout;
import java.awt.Point;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import javax.swing.InputMap;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.KeyStroke;
import javax.swing.RowSorter;
import javax.swing.SortOrder;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableModel;
import javax.swing.table.TableRowSorter;

import config.ConfigInterface;

@SuppressWarnings("serial")
public class SyncTable extends JPanel {

	private final int COLUMNS = 9;
	private final int COLUMN_LOCAL_PATH  = 2;
	private final int COLUMN_REMOTE_PORT = 4;
	private final int COLUMN_REMOTE_PATH = 5;
	private String[] columnConfigNames = {null,              "Enabled", "LocalPath",  "RemoteAddress",        "RemotePort",  "RemotePath",  "AllowDelete",  "UseDeleteDialog",   "IncludeHiddenFiles"};
	private String[] columnNames       = {"#",               "Enabled", "Local Path", "Remote Address",       "Remote Port", "Remote Path", "Allow Delete", "Use Delete Dialog", "Include Hidden Files"};
	private String[] columnToolTips    = {"Priority Number", "Enabled", "Local Path", "Remote IP or Address", "Remote Port", "Remote Path", "Allow Delete", "Use Delete Dialog", "Include Hidden Files in Sync"};

	private ActionInterface actionInterface;
	private TableRowSorter<SyncTableModel> sorter;
	private JTable table;


	public SyncTable(ConfigInterface config, ActionInterface actionInterface) {
		this.actionInterface = actionInterface;
		init(config);
	}

	private void init(ConfigInterface config) {
		setLayout(new GridLayout(1,0));

		SyncTableModel syncTableModel = new SyncTableModel(config, columnNames, columnConfigNames);
		sorter = new TableRowSorter<SyncTableModel>(syncTableModel);
		table = new JTable(syncTableModel) { // Color every other row light blue

			// Make every other row light blue
			public Component prepareRenderer(TableCellRenderer renderer, int row, int column) {
				Component c = super.prepareRenderer(renderer, row, column);
				if(row % 2 == 1 && !isCellSelected(row, column)) {
					c.setBackground(new Color(243, 249, 255)); // Light blue
				}
				else if(!isCellSelected(row, column)) {
					c.setBackground(getBackground());
				}
				return c;
			}

			// Set tool tip for cells.
			public String getToolTipText(MouseEvent e) {
				String tip = null;
				Point p = e.getPoint();
				int rowIndex = rowAtPoint(p);
				int colIndex = columnAtPoint(p);
				int realColumnIndex = convertColumnIndexToModel(colIndex);

				if(realColumnIndex == COLUMN_LOCAL_PATH || realColumnIndex == COLUMN_REMOTE_PATH ) {
					if(rowIndex != -1) {
						tip = (String)getValueAt(rowIndex, colIndex);
						if(tip != null && !tip.equals("")) {
							return tip;
						}
					}
				}
				else {
					return super.getToolTipText(e);
				}
				return null;
			}

			// Implement table header tool tip 
			protected JTableHeader createDefaultTableHeader() {
				return new JTableHeader(columnModel) {
					public String getToolTipText(MouseEvent e) {
						Point p = e.getPoint();
						int index = columnModel.getColumnIndexAtX(p.x);
						int realIndex = columnModel.getColumn(index).getModelIndex();
						return columnToolTips[realIndex];
					}
				};
			}
		};
		table.setFillsViewportHeight(true);
		table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
		table.setShowGrid(false);
		table.setRowSorter(sorter);
		table.getSelectionModel().addListSelectionListener(listSelectionListener);
		table.getModel().addTableModelListener(tableModelListener);

		// Set renderer's and editor's 
		SyncTableFolderRenderer syncTableFolderRenderer = new SyncTableFolderRenderer();
		table.getColumnModel().getColumn(COLUMN_LOCAL_PATH).setCellRenderer(syncTableFolderRenderer);
		table.getColumnModel().getColumn(COLUMN_LOCAL_PATH).setCellEditor(new SyncTableFolderEditor(syncTableFolderRenderer, COLUMN_LOCAL_PATH));
		table.getColumnModel().getColumn(COLUMN_REMOTE_PORT).setCellEditor(new SyncTablePortEditor());

		// Change behavior of enter key to move one cell to the right instead of down
		KeyStroke enter = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0);
		InputMap inputMap = table.getInputMap(JTable.WHEN_FOCUSED);
		inputMap.put(enter, "selectNextColumnCell");

		setColumnWidths(config.getVariableString("gui", "JavaColumnWidths"));
		setColumnPositions(config.getVariableString("gui", "JavaColumnPositions"));
		setRowSortOrder(config.getVariableString("gui", "JavaRowSortOrder")); 

		JScrollPane scrollPane = new JScrollPane(table);

		add(scrollPane);
	}

	private void setColumnWidths(String columnWidths) {
		String[] columnWidthsSplit = columnWidths.split(",");
		for (int i = 0; i < columnWidthsSplit.length; i++) {
			try {
				TableColumn column = table.getColumnModel().getColumn(i);
				int realIndex = column.getModelIndex();
				int preferredWidth = new Integer(columnWidthsSplit[realIndex]);
				column.setPreferredWidth(preferredWidth);
			} catch(Exception e) {
				e.printStackTrace();
			}
		}
	}

	private void setColumnPositions(String columnPositions) {
		try {
			// Init stuff
			String[] columnPositionsSplit = columnPositions.split(",");
			int length = columnPositionsSplit.length;
			int[] columnPos = new int[length];
			for(int i = 0; i < length; i++) {
				columnPos[i] = new Integer(columnPositionsSplit[i]);
			}

			// Fix positions loop
			while(true) {
				int[] currentPos = getColumnPositions();
				// Determine if we are done
				boolean done = true;
				for(int i = 0; i < length; i++) {
					if( columnPos[i] != currentPos[i] ) {
						done = false;
					}
				}
				if(done) {
					break;
				}

				// Find one column to move (the one that is farthest away from where it is supposed to be)
				int greatestDiff = 0;
				int greatestDiffPos = 0;
				for(int i = 0; i < length; i++) {
					int tempDiff = Math.abs(columnPos[i]-currentPos[i]);
					if( tempDiff > greatestDiff ) {
						greatestDiff = tempDiff;
						greatestDiffPos = i;
					}
				}
				// Move the column
				if(greatestDiff > 0 ) {
					int fromIndex = -1;
					for(int i = 0; i < length; i++) {
						int temp = table.getColumnModel().getColumn(i).getModelIndex();
						if(temp == greatestDiffPos) {
							fromIndex = i;
						}
					}
					table.moveColumn(fromIndex, columnPos[greatestDiffPos]);
				}
			}
		} catch(Exception e) {
			e.printStackTrace();
		}
	}

	private void setRowSortOrder(String rowSortOrder) {
		try {
			if(rowSortOrder.equals("")) {
				return;
			}
			if(rowSortOrder.startsWith("0;ASCENDING")) {
				return;
			}
			List <RowSorter.SortKey> sortKeys = new ArrayList<RowSorter.SortKey>();
			String[] rowSortOrderSplit = rowSortOrder.split(",");
			for(int i = 0; i < rowSortOrderSplit.length; i++) {
				String[] info = rowSortOrderSplit[i].split(";");
				int column = new Integer(info[0]);
				String sortOrder = info[1];
				if(sortOrder.equals("ASCENDING")) {
					sortKeys.add(new RowSorter.SortKey(column, SortOrder.ASCENDING));
				}
				else if(info[1].equals("DESCENDING")) {
					sortKeys.add(new RowSorter.SortKey(column, SortOrder.DESCENDING));
				}
				else {
					sortKeys.add(new RowSorter.SortKey(column, SortOrder.UNSORTED));
				}
			}
			sorter.setSortKeys(sortKeys); 
		} catch(Exception e) {
			e.printStackTrace();
		}
	}



	// Public functions //

	public int[] getSelectedRows() {
		int[] viewRows = table.getSelectedRows();
		int[] modelRows = new int[viewRows.length];
		for(int i = 0; i < viewRows.length; i++) {
			modelRows[i] = table.convertRowIndexToModel(viewRows[i]);
		}
		Arrays.sort(modelRows);
		return modelRows;
	}

	public int getLeadSelectionRow() {
		int viewSelection = table.getSelectionModel().getLeadSelectionIndex();
		if(viewSelection == -1) {
			return viewSelection;
		}
		else{
			return table.convertRowIndexToModel(viewSelection);
		}
	}

	public void addRow(int index) {
		((SyncTableModel)table.getModel()).fireTableRowsInserted(index, index);
	}

	public void removeRow(int index) {
		((SyncTableModel)table.getModel()).fireTableRowsDeleted(index, index);
	}

	public void moveUp(int index) {
		((SyncTableModel)table.getModel()).fireTableRowsUpdated(index-1, index);
		int newIndex = table.convertRowIndexToView(index-1);
		table.setRowSelectionInterval(newIndex, newIndex);
	}

	public void moveDown(int index) {
		((SyncTableModel)table.getModel()).fireTableRowsUpdated(index, index+1);
		int newIndex = table.convertRowIndexToView(index+1);
		table.setRowSelectionInterval(newIndex, newIndex);
	}

	public int[] getColumnWidths() {
		int[] columnWidths = new int[COLUMNS];
		for(int i = 0; i < COLUMNS; i++) {
			TableColumn column = table.getColumnModel().getColumn(i);
			int realIndex = column.getModelIndex();
			columnWidths[realIndex] = column.getWidth();
		}
		return columnWidths;
	}

	public int[] getColumnPositions() {
		int[] columnWidths = new int[COLUMNS];
		for(int i = 0; i < COLUMNS; i++) {
			TableColumn column = table.getColumnModel().getColumn(i);
			int realIndex = column.getModelIndex();
			columnWidths[realIndex] = i;
		}
		return columnWidths;
	}

	public int getRowCount() {
		return ((SyncTableModel)table.getModel()).getRowCount();
	}

	public List<? extends RowSorter.SortKey> getSortKeys() {
		List<? extends RowSorter.SortKey> sortKeys = sorter.getSortKeys();
		return sortKeys;
	}





	ListSelectionListener listSelectionListener = new ListSelectionListener() {

		public void valueChanged(ListSelectionEvent e) {
			if (e.getValueIsAdjusting()) {
				return;
			}
			actionInterface.enableDisableComponents();
		}
	};

	TableModelListener tableModelListener = new TableModelListener() {

		public void tableChanged(TableModelEvent e) {
			int row = e.getFirstRow();
			switch (e.getType()) {
			case TableModelEvent.INSERT:
				System.out.println("New row added: " + row);
				break;
			case TableModelEvent.DELETE:
				System.out.println("Row deleted: " + row);
				break;
			case TableModelEvent.UPDATE:
				int column = e.getColumn();
				if(column == -1) {
					System.out.println("Rows where moved: " + row);
				}
				else {
					TableModel model = (TableModel)e.getSource();
					String columnName = model.getColumnName(column);
					Object data = model.getValueAt(row, column);
					System.out.println("Cell updated: columnName=" + columnName + ", row=" + row + ", data=" + data);
				}
				break;
			}
		}
	};

}
